package org.ainlolcat.ainscience.metaentity;

import org.ainlolcat.ainscience.Controller;
import org.ainlolcat.ainscience.UpdateListener;
import org.ainlolcat.ainscience.column.Column;
import org.ainlolcat.ainscience.entity.*;
import org.ainlolcat.ainscience.entity.links.Link;
import org.ainlolcat.ainscience.matrix.*;
import org.ainlolcat.ainscience.project.ProjectFileHandler;
import org.ainlolcat.ainscience.tools;
import org.ainlolcat.ainscience.utils.PathUtils;
import org.ainlolcat.ctp.util.IDGenerator;
import org.apache.log4j.Logger;
import org.jdom.Element;

import java.io.IOException;
import java.util.*;

/**
 * @author ainlolcat
 * Date: 12/8/11
 */
public class MulticolumnMetaEntityParser extends AbstractMetaEntityParser{
    private static Logger log = Logger.getLogger(MulticolumnMetaEntityParser.class);
    String basename;

    @Override
    protected MetaEntity loadInternal() throws IOException {
        String file = desc.getAttributeValue("file");
        basename = metaEntityDescription.getChild("basename").getAttributeValue("name");
        file = PathUtils.replaceMacros(file);

        loadInternalFromFile(file);

        MulticolumnMetaEntity sme = new MulticolumnMetaEntity(type, file, template);
        sme.setID(IDGenerator.getNextIntegerID());
        sme.setName(name);
        return sme;
    }

    @Override
    protected int loadInternalFromFile(String filename) throws IOException {
        return loadInternalFromFile(filename, 0);
    }

    protected int loadInternalFromFile(String filename, int numberShift) throws IOException {
        MatrixFile matrixs = new MatrixFileParser().parseFile(template.getMatrixParserParams(), filename);
        Matrix matrix = matrixs.getMatrix(0);

        List<Element> columns =  metaEntityDescription.getChildren("column");
        boolean hasNext = true;
        int currentNum = numberShift;
        while(hasNext){
            hasNext = handleNextColumnSet(columns, matrix, filename, currentNum);
            if (!hasNext)
                break;
            else
                currentNum++;
        }
        return currentNum;
    }

    protected boolean handleNextColumnSet(List<Element> columns, Matrix source, String filename, int currentNum){
        boolean hasNext = true;
        Matrix temp = new Matrix();
        for (Element e: columns){
            hasNext = handleColumnDefinition(e, currentNum, source, temp);
            if (!hasNext)
                break;
        }
        if (!hasNext)
            return hasNext;

        Map<String, Link> links = tools.parseLinks(desc.getChildren("link"), new MatrixEntityWrapper(temp));

        new MatrixCalculator().calculate(temp, metaEntityDescription, links);

        try{
            Element e = prepareEntityDefinition( basename, currentNum, type2reg, type2regTemplate, desc.getChildren());
            Entity se = new SimpleEntityParser().load(temp,e);
            Controller.getInstance().getProject().addEntity(se);
            Controller.getInstance().throwUpdateEvent(se, UpdateListener.CREATE_ENTITY_ACTION);
        } catch (Exception ex){
            log.error("Cannot load next entity in " +
                    "\n\tFile: " + filename +
                    "\n\tColumn count: " + temp.getColumnCount()
                    , ex);
        }
        return hasNext;
    }

    protected boolean handleColumnDefinition(Element e, int currentNum, Matrix source, Matrix temp )
    {
        boolean hasNext = true;
        String iterType = e.getAttributeValue("iterType");
        //<column 	id="1"	axis="y0" 	type="1"	iterType="number" 		value="0"/>
        if (iterType.equals("number")){
            Integer number = tools.parseInteger(e.getAttributeValue("value"));
            Column c0 = source.getColumn(number);
            if (c0!=null){
                c0.setAxis(e.getAttributeValue("axis"));
                c0.setType(tools.parseInteger(e.getAttributeValue("type")));
                temp.setColumn(tools.parseInteger(e.getAttributeValue("id")),c0);
            } else {
                hasNext = false;
            }
        }
        //<column 	id="0"	axis="x0" 	type="1"	iterType="everyAfter"  	value="0" since="1"/>
        else if (iterType.equals("everyAfter")){
            Integer add = tools.parseInteger(e.getAttributeValue("add"));
            Integer mult = tools.parseInteger(e.getAttributeValue("mult"));
            Integer since = tools.parseInteger(e.getAttributeValue("since"));
            Column c0 = source.getColumn(since + add + mult * currentNum);
            if (c0!=null){
                c0.setAxis(e.getAttributeValue("axis"));
                c0.setType(tools.parseInteger(e.getAttributeValue("type")));
                temp.setColumn(tools.parseInteger(e.getAttributeValue("id")),c0);
            } else {
                hasNext = false;
            }
        }
        //<column 	id="0"	axis="x0" 	type="1"	iterType="everyAfter"  	value="0" since="1"/>
        else if (iterType.equals("every")){
            Integer add = tools.parseInteger(e.getAttributeValue("add"));
            Integer mult = tools.parseInteger(e.getAttributeValue("mult"));
            Column c0 = source.getColumn(add + mult * currentNum);
            if (c0!=null){
                c0.setAxis(e.getAttributeValue("axis"));
                c0.setType(tools.parseInteger(e.getAttributeValue("type")));
                temp.setColumn(tools.parseInteger(e.getAttributeValue("id")),c0);
            } else {
                hasNext = false;
            }
        }
        return hasNext;
    }

    //<metaentity name="Entity Loader" id="401" templateID="103" entityType="spectr_loader" path="%HOME%/spectr/"/>
    public Element save(MetaEntity current) throws UnsupportedMetaEntityTypeException {
        if (current instanceof MultifileMetaEntity){
            Element entity = new Element(ProjectFileHandler.META_ENTITY_TAG);
            entity.setAttribute(ProjectFileHandler.ENTITY_ATTR_NAME,current.getName());
            entity.setAttribute(ProjectFileHandler.ENTITY_ATTR_ID, current.getID().toString());
            entity.setAttribute(ProjectFileHandler.ENTITY_ATTR_TEMPLATE_NAME, current.getTemplate().getName());
            entity.setAttribute(ProjectFileHandler.ENTITY_ATTR_ENTITY_TYPE, current.getType());
            entity.setAttribute(ProjectFileHandler.ENTITY_ATTR_FILE, ((MulticolumnMetaEntity) current).getFile());
            return entity;
        } else {
            throw new UnsupportedMetaEntityTypeException();
        }
    }
}
